package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"
)



/*

// 64 + 64 = 128 bytes

struct spi_nand_info_t {
	uint32_t version;
	uint32_t id;
	uint32_t page_size;
	uint32_t spare_size;
	
	uint32_t block_size;
	uint32_t pages_per_block;
	uint32_t fip_block_cnt;
	uint8_t pages_per_block_shift;
	uint8_t badblock_pos;
	uint8_t dummy_data1[2];
	
	uint32_t flags;
	uint8_t ecc_en_feature_offset;
	uint8_t ecc_en_mask;
	uint8_t ecc_status_offset;
	uint8_t ecc_status_mask;
	uint8_t ecc_status_shift;
	uint8_t ecc_status_uncorr_val;
	uint8_t dummy_data2[2];
	uint32_t erase_count; // erase count for sys base block
	
	uint8_t sck_l;
	uint8_t sck_h;
	uint16_t max_freq;
	uint32_t sample_param;
	uint8_t xtal_switch;
	uint8_t dummy_data3[71];
};



struct spinor_info_t {
	uint32_t ctrl;
	uint32_t dly_ctrl;
	uint32_t tran_csr;
	uint32_t opt;
	
	uint32_t reserved_1;
	uint32_t reserved_2;
	uint32_t reserved_3;
	uint32_t reserved_4;
	uint32_t reserved_5;
	
} __packed;

struct chip_conf {
	uint32_t reg;
	uint32_t value;
} __packed;

struct fip_flags {
	struct {
		uint8_t rsa_size : 2;
		uint8_t scs : 2;
		uint8_t encrypted : 2;
		uint8_t reserved1 : 2;
	};
	uint8_t reserved2[7];
} __packed;


struct fip_param1 {
	
	uint64_t magic1;
	uint32_t magic2;
	uint32_t param_cksum;
	
	struct spi_nand_info_t nand_info;			// ofs= 16, size=128 bytes
	struct spinor_info_t spinor_info;			// ofs=144, size= 36 bytes
	struct fip_flags fip_flags;					// ofs=180, size=  8 bytes
	
	uint32_t chip_conf_size;					// ofs=188=0xBC

	uint32_t blcp_img_cksum;					// ofs=0xC0
	uint32_t blcp_img_size;						// ofs=0xC4
	uint32_t blcp_img_runaddr;					// ofs=0xC8
	uint32_t blcp_param_loadaddr;
	uint32_t blcp_param_size;

	uint32_t bl2_img_cksum;						// ofs=0xD4
	uint32_t bl2_img_size;						// ofs=0xD8

	uint32_t bld_img_size;
	uint32_t param2_loadaddr;					// ofs=0xE0
	uint32_t reserved1;
	
	struct chip_conf chip_conf[95];				// ofs=232, size=760 bytes

	uint8_t bl_ek[32];							// ofs=992

	uint8_t root_pk[512];						// ofs=1024
	uint8_t bl_pk[512];

	uint8_t bl_pk_sig[512];
	uint8_t chip_conf_sig[512];

	uint8_t bl2_img_sig[512];
	uint8_t blcp_img_sig[512];
	
} __packed __aligned(__alignof__(unsigned int));


*/




/*
param-1 packer :
	 fp : 输出文件指针.
	blk : fsbl.bin 的文件内容.
return :
	total size for param-1 and fsbl.bin
*/
func mkfp1( crc hash.Hash32, blk []byte, fp io.Writer ) (uint32, error) {
	
	var padblk []byte

	// header : 4096 bytes
	hdr := bytes.Repeat( []byte{0}, 4096 )

	// MAGIC1
	binary.LittleEndian.PutUint32( hdr[0:], 0x4C425643 )
	binary.LittleEndian.PutUint32( hdr[4:], 0xA3130 )

	// NOR INFO
	for i:=0; i<36; i++  {
		hdr[0x90 + i] = 0xFF
	}

	// CHIP_CONF_SIZE = 0x2F8
	binary.LittleEndian.PutUint32( hdr[0xBC:], 0x2F8 )

	// BLCP_IMG_CKSUM, BLCP_IMG_SIZE, BLCP_IMG_RUNADDR
	binary.LittleEndian.PutUint32( hdr[0xC0:], 0xCAFE0000 )
	binary.LittleEndian.PutUint32( hdr[0xC4:], 0 )
	binary.LittleEndian.PutUint32( hdr[0xC8:], 0x5200200 )

	// BL2_IMG_CKSUM, BL2_IMG_SIZE, padding to 512
	crc.Reset()
	crc.Write( blk )

	if ( len(blk) % 512 ) != 0  {
		tsiz := 512 - ( len(blk) % 512 )
		padblk = bytes.Repeat( []byte{0}, tsiz )
		crc.Write( padblk )
	}

	binary.LittleEndian.PutUint32( hdr[0xD4:], crc.Sum32() )
	binary.LittleEndian.PutUint32( hdr[0xD8:], uint32(len(blk) + len(padblk)) )

	// PARAM2_LOADADDR
	binary.LittleEndian.PutUint32( hdr[0xE0:], 0 )
	
	// CHIP_CONF : { addr, value } 若干寄存器配置.
	binary.LittleEndian.PutUint32( hdr[0xE8:], 0x0E00000C )
	binary.LittleEndian.PutUint32( hdr[0xEC:], 0xA0000001 )
	binary.LittleEndian.PutUint32( hdr[0xF0:], 0x0E00000C )
	binary.LittleEndian.PutUint32( hdr[0xF4:], 0xA0000002 )
	binary.LittleEndian.PutUint32( hdr[0xF8:], 0xFFFFFFA0 )
	binary.LittleEndian.PutUint32( hdr[0xFC:], 0xFFFFFFFF )

	// PARAM_CKSUM
	crc.Reset()
	crc.Write( hdr[16:0x800] )
	binary.LittleEndian.PutUint32( hdr[0xC:], crc.Sum32() )

	//
	fp.Write( hdr )
	fp.Write( blk )

	if len(padblk) != 0  {
		fp.Write( padblk )
	}

	return uint32(len(blk) + len(padblk) + len(hdr)), nil
}


/*
param-2 packer :
	 fp : 输出文件指针.
	ofs : 已经输出的文件字节数, 作为后续字段的 偏移量.
	blk : ddr-param 的文件内容.

return:
	total size for param-2 and ddr.bin
*/
func mkfp2( crc hash.Hash32, ofs uint32, blk []byte, fp io.Writer ) (uint32, error) {
		
	// header : 4096 bytes
	hdr := bytes.Repeat( []byte{0}, 4096 )

	// MAGIC1
	binary.LittleEndian.PutUint32( hdr[0:], 0x444C5643 )
	binary.LittleEndian.PutUint32( hdr[4:], 0xA3230 )

	// DDR_OARAN
	crc.Reset()
	crc.Write( blk )
	binary.LittleEndian.PutUint32( hdr[0x10:], crc.Sum32() )
	binary.LittleEndian.PutUint32( hdr[0x14:], ofs + uint32( len(hdr) ) )
	binary.LittleEndian.PutUint32( hdr[0x18:], uint32( len(blk) ) )
	binary.LittleEndian.PutUint32( hdr[0x1C:], 0 )

	// BLCP_2ND ( cvirots / freertos )
	binary.LittleEndian.PutUint32( hdr[0x20:], 0xCAFE26A7 )			// CHECKSUM
	binary.LittleEndian.PutUint32( hdr[0x24:], 0xF800 )				// LOADADDR ( offset )
	binary.LittleEndian.PutUint32( hdr[0x28:], 0x3600 )				// SIZE
	binary.LittleEndian.PutUint32( hdr[0x2C:], 0x83F40000 )			// RUNADDR

	// MONITOR ( opensbi )
	binary.LittleEndian.PutUint32( hdr[0x30:], 0xCAFE8530 )			// CHECKSUM
	binary.LittleEndian.PutUint32( hdr[0x34:], 0x12E00 )			// LOADADDR ( offset )
	binary.LittleEndian.PutUint32( hdr[0x38:], 0x1B000 )			// SIZE
	binary.LittleEndian.PutUint32( hdr[0x3C:], 0x80000000 )			// RUNADDR

	// LOADER_2ND ( uboot )
	binary.LittleEndian.PutUint32( hdr[0x40:], 0 )
	binary.LittleEndian.PutUint32( hdr[0x44:], 0x2DE00 )
	binary.LittleEndian.PutUint32( hdr[0x48:], 0 )
	binary.LittleEndian.PutUint32( hdr[0x4C:], 0 )

	// header checksum
	crc.Reset()
	crc.Write( hdr[12:] )
	binary.LittleEndian.PutUint32( hdr[8:], crc.Sum32() )

	// 
	fp.Write( hdr )
	fp.Write( blk )

	// 
	return ofs + uint32(len(hdr) + len(blk)), nil
}





func main()  {

	if len( os.Args ) != 2  {
		fmt.Println( "args : fsbl.bin " );
		return
	}

	ffp, err := os.Open( os.Args[1] )
	if err != nil {
		fmt.Println("open fsbl fail:", err )
		return
	}

	defer ffp.Close()
	fsbl, err := ioutil.ReadAll( ffp )
	if err != nil {
		fmt.Println("read fsbl fail:", err )
		return
	}
	
	// open out file : fip.bin
	bfp, err := os.Create( "fip.bin" )
	if err != nil {
		fmt.Println("create fip.bin fail:", err )
		return
	}

	defer bfp.Close()

	crc := NewCCITT()
	tsiz, err := mkfp1( crc, fsbl, bfp )
	if err != nil {
		fmt.Println( "mk param-1 fail:", err )
		return 
	}

	//
	fmt.Printf( "fip.bin succ, total size: %v\n", tsiz )
	return
}

